/*
 *MIT License
 *
 *Copyright (c) 2019 chenshuai cs4380@163.com
 *
 *Permission is hereby granted, free of charge, to any person obtaining a copy
 *of this software and associated documentation files (the "Software"), to deal
 *in the Software without restriction, including without limitation the rights
 *to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *copies of the Software, and to permit persons to whom the Software is
 *furnished to do so, subject to the following conditions:
 *
 *The above copyright notice and this permission notice shall be included in all
 *copies or substantial portions of the Software.
 *
 *THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 *SOFTWARE.
 */

package com.cs.cslc.admin.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.cs.cslc.admin.dto.MenuButtonCodeDTO;
import com.cs.cslc.admin.dto.MenuButtonDTO;
import com.cs.cslc.admin.dto.UserInfoDTO;
import com.cs.cslc.admin.entity.BaseUser;
import com.cs.cslc.admin.entity.BaseUserRole;
import com.cs.cslc.admin.entity.SysMenu;
import com.cs.cslc.admin.entity.SysRoleAuthorization;
import com.cs.cslc.admin.mapper.BaseUserMapper;
import com.cs.cslc.common.biz.BaseBusinessBiz;
import com.cs.cslc.common.constant.CommonConstant;
import com.cs.cslc.common.constant.HttpStatusConstant;
import com.cs.cslc.common.constant.InitialCapacityConstant;
import com.cs.cslc.common.context.BaseContextHandler;
import com.cs.cslc.common.msg.ObjectResult;
import com.cs.cslc.common.msg.TableResult;
import com.cs.cslc.common.pojo.ParamQuery;
import com.cs.cslc.common.util.BeanUtil;
import com.cs.cslc.common.util.Sha256Util;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import java.util.stream.Collectors;

/**
 * BaseUser 基础用户.
 *
 * @author cs4380 https://gitee.com/xhbug_cs4380  cs4380@163.com
 * @version 1.0
 * @since 2019-09-30 15:23:58
 */
@Service
public class BaseUserBiz extends BaseBusinessBiz<BaseUserMapper, BaseUser> {

    @Autowired
    private BaseUserRoleBiz baseUserRoleBiz;

    @Autowired
    private SysRoleAuthorizationBiz roleAuthorizationBiz;

    @Autowired
    private SysMenuBiz sysMenuBiz;

    @Autowired
    private SysMenuButtonBiz menuButtonBiz;

    @Override
    public boolean insertModel(BaseUser entity) {
        String password = entity.getPassword();
        // Base64解码
        password = new String(Base64.getDecoder().decode(password));
        // 密码加密
        entity.setPassword(Sha256Util.getSHA256(password));
        return super.insertModel(entity);
    }

    @Override
    public TableResult<BaseUser> selectTableByParamQuery(ParamQuery query) {
        // 排除超级管理员
        query.put("is_super_admin", CommonConstant.IS_TRUE);
        String[] fields = new String[]{"id", "account", "user_name", "is_disabled", "tenant_id", "create_time", "update_time"};
        return super.selectTableByParamQuery(query, fields);
    }

    /**
     * 通过用户账号获取用户信息
     * <p>
     * 排除逻辑删除，禁用的账号
     * </p>
     *
     * @param account 用户账号
     * @return 包含用户全部基本信息
     */
    public BaseUser getBaseUserByAccount(String account) {
        if (StringUtils.isBlank(account)) {
            return new BaseUser();
        }
        LambdaQueryWrapper<BaseUser> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(BaseUser::getAccount, account);
        return this.baseMapper.selectOne(queryWrapper);
    }

    /**
     * 查询用户基本信息
     *
     * @param userId 用户主键
     * @return 只包含用户基本信息
     */
    public BaseUser getUserInfoByUserId(String userId) {
        LambdaQueryWrapper<BaseUser> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(BaseUser::getId, userId);
        // 排除禁用用户
        queryWrapper.ne(BaseUser::getIsDisabled, CommonConstant.IS_FALSE);
        return this.baseMapper.selectOne(queryWrapper);
    }

    /**
     * 获取用户信息
     * <p>
     * 获取用户基本信息
     * </p>
     *
     * @return 用户基本信息
     */
    public UserInfoDTO getUserInfo() {
        UserInfoDTO userInfoDTO = new UserInfoDTO();
        // 组装用户基本信息
        BaseUser baseUser = this.getUserInfoByUserId(BaseContextHandler.getUserId());
        userInfoDTO.setBaseUser(baseUser);
        return userInfoDTO;
    }

    /**
     * 用户权限信息
     * <p>
     * 菜单权限、按钮权限
     * </p>
     *
     * @return 用户权限信息
     */
    public UserInfoDTO getUserPermission() {
        UserInfoDTO userInfoDTO = new UserInfoDTO();

        // 判断是否超级管理员
        Integer isSuperAdmin = 1;

        // 查询用户的角色菜单权限
        List<SysMenu> sysMenuList;

        // 查询用户的角色按钮权限
        List<MenuButtonCodeDTO> menuButtonList;

        // 组装用户角色信息
        if (isSuperAdmin.equals(BaseContextHandler.getIsSuperAdmin())) {
            // 获取系统所有权限
            sysMenuList = sysMenuBiz.getAllMenuList();
            menuButtonList = menuButtonBiz.getAllMenuCodeAndButCode();
        } else {
            // 普通用户，查询直接角色集的权限
            List<String> roles = baseUserRoleBiz.getRolesByUserId(BaseContextHandler.getUserId());
            List<SysRoleAuthorization> roleAuthList = roleAuthorizationBiz.getRoleAuthByRoleCodes(roles);

            // 获取角色对应的菜单ids和按钮ids
            List<String> menuIdList = new ArrayList<>(InitialCapacityConstant.INITIAL_256_NUMBER);
            List<Integer> menuButtonIds = new ArrayList<>(InitialCapacityConstant.INITIAL_512_NUMBER);
            // 获取角色权限集
            roleAuthList.forEach(roleAuth -> {
                if (SysRoleAuthorization.MENU_RESOURCE_TYPE.equals(roleAuth.getResourceType())) {
                    menuIdList.add(roleAuth.getResourceId());
                } else {
                    menuButtonIds.add(Integer.valueOf(roleAuth.getResourceId()));
                }
            });

            // 查询用户的角色权限
            sysMenuList = sysMenuBiz.getMenuListByMenuIds(menuIdList);
            menuButtonList = menuButtonBiz.getMenuCodeAndButCodeByButIds(menuButtonIds);
        }

        // 组装用户的角色菜单权限
        userInfoDTO.setMenuTree(sysMenuBiz.getMenuTreeSysMenuTree(sysMenuList));
        // 组装用户的角色按钮权限
        userInfoDTO.setMenuButton(this.generateMenuButton(menuButtonList));
        return userInfoDTO;
    }

    /**
     * 菜单和按钮管理列表转换json格式
     *
     * @param menuButtonList 菜单和按钮管理列表
     *                       数据格式：
     *                       [
     *                       menuCode1:butCode1
     *                       menuCode1:butCode2
     *                       ]
     * @return 数据格式：
     * menuButton:
     * {
     * menuCode1:{ butCode1:1, butCode2:1, ... }，
     * menuCode2:{ butCode1:1, butCode2:1, ... },
     * ...
     * }
     */
    private MenuButtonDTO<String, MenuButtonDTO<String, Integer>> generateMenuButton(
            List<MenuButtonCodeDTO> menuButtonList) {
        MenuButtonDTO<String, MenuButtonDTO<String, Integer>> menuButton = new MenuButtonDTO<>();
        for (MenuButtonCodeDTO menuButtonCodeDTO : menuButtonList) {
            // 菜单编码
            String menuCode = menuButtonCodeDTO.getMenuCode();
            // 关系数据丢失则跳过
            if (StringUtils.isBlank(menuCode)) {
                continue;
            }
            // 按钮编码
            String buttonCode = menuButtonCodeDTO.getButtonCode();

            MenuButtonDTO<String, Integer> tmpeMenuButtonDTO = menuButton.get(menuCode);
            if (BeanUtil.isEmpty(tmpeMenuButtonDTO)) {
                tmpeMenuButtonDTO = new MenuButtonDTO<>();
            }
            // value值只是为了前端使用，不做其他处理
            tmpeMenuButtonDTO.put(buttonCode, 1);
            menuButton.put(menuCode, tmpeMenuButtonDTO);
        }
        return menuButton;
    }

    /**
     * 修改密码
     *
     * @param password    原始密码
     * @param newPassword 新密码
     */
    public ObjectResult<BaseUser> changePassword(String password, String newPassword) {
        ObjectResult<BaseUser> result = new ObjectResult<>();
        // Base64解码
        password = new String(Base64.getDecoder().decode(password));
        newPassword = new String(Base64.getDecoder().decode(newPassword));

        // 加密后
        password = Sha256Util.getSHA256(password);
        newPassword = Sha256Util.getSHA256(newPassword);

        // 通过主键和原始密码查询记录
        String userId = BaseContextHandler.getUserId();
        LambdaQueryWrapper<BaseUser> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(BaseUser::getId, userId);
        queryWrapper.eq(BaseUser::getPassword, password);
        Long count = this.baseMapper.selectCount(queryWrapper);
        if (count <= 0) {
            result.setStatus(HttpStatusConstant.FAIL);
            result.setMessage("原始密码不正确请重新输入！");
            return result;
        }

        BaseUser user = new BaseUser();
        user.setId(userId);
        // 更新密码
        user.setPassword(newPassword);
        this.baseMapper.updateById(user);
        // TODO 密码修改完成，更新用户token缓存
        return result;
    }

    /**
     * 通过部门编码获取用户列表
     *
     * @param deptCode 部门编码
     * @return 指定部门的用户列表
     */
    public TableResult<BaseUser> getDeptUsersByDeptCode(String deptCode, int page, int limit) {
        if (StringUtils.isBlank(deptCode)) {
            return new TableResult<>(0, new ArrayList<>());
        }
        IPage<BaseUser> iPage = this.baseMapper.selectDeptUsersByDeptCode(new Page<>(page, limit), deptCode);
        List<BaseUser> userList = iPage.getRecords();
        if (BeanUtil.isNotEmpty(userList)) {
            return new TableResult<>(iPage.getTotal(), userList);
        } else {
            return new TableResult<>(0, new ArrayList<>());
        }
    }

    /**
     * 获取用户角色关系
     *
     * @return 角色列表
     */
    public List<String> getUserRolesByUserId(String userId) {
        List<BaseUserRole> userRoleList = baseUserRoleBiz.getUserRoleByUserId(userId);
        List roleList = userRoleList.stream().map(userRole -> userRole.getRoleCode()).collect(Collectors.toList());
        return roleList;
    }

    /**
     * 设置用户角色关系
     *
     * @param roleCodes 角色编码集
     */
    @Transactional(rollbackFor = {Exception.class, RuntimeException.class})
    public void setUserRole(String userId, List<String> roleCodes) {
        // 删除用户关系
        baseUserRoleBiz.delUserRoleByRoleCodes(userId);
        // 新增用户关系
        baseUserRoleBiz.addUserRole(userId, roleCodes);
    }

    /**
     * 排除指定部门的用户列表
     *
     * @param excludeDeptCode 排除指定部门
     * @param page            页码
     * @param limit           页容量
     * @return 获取所有部门用户列表
     */
    public TableResult<BaseUser> getUsersExcludeDept(String excludeDeptCode, int page, int limit) {
        IPage<BaseUser> iPage = this.baseMapper.selectUsersExcludeDept(new Page<>(page, limit), excludeDeptCode);
        List<BaseUser> userList = iPage.getRecords();
        if (BeanUtil.isNotEmpty(userList)) {
            return new TableResult<>(iPage.getTotal(), userList);
        } else {
            return new TableResult<>(0, new ArrayList<>());
        }
    }
}